import { LineGradient } from '../gradient/lineGradient'
import Matrix from '../matrix'

export type IDirection = 'ltr' | 'rtl' | 'inherit'
export type CSSLength = string
export type CSSColor = string

export type IFontKerning = 'auto' | 'normal' | 'none'
export type IFontStretch =
  | 'ultra-condensed'
  | 'extra-condensed'
  | 'condensed'
  | 'semi-condensed'
  | 'normal'
  | 'semi-expanded'
  | 'expanded'
  | 'extra-expanded'
  | 'ultra-expanded'
export type IFontVariantCaps =
  | 'normal'
  | 'small-caps'
  | 'all-small-caps'
  | 'petite-caps'
  | 'all-petite-caps'
  | 'unicase'
  | 'titling-caps'
export type IGlobalCompositeOperation =
  | 'source-over'
  | 'source-in'
  | 'source-out'
  | 'source-atop'
  | 'destination-over'
  | 'destination-in'
  | 'destination-out'
  | 'destination-atop'
  | 'lighter'
  | 'copy'
  | 'xor'
  | 'multiply'
  | 'screen'
  | 'overlay'
  | 'darken'
  | 'lighten'
  | 'color-dodge'
  | 'color-burn'
  | 'hard-light'
  | 'soft-light'
  | 'difference'
  | 'exclusion'
  | 'hue'
  | 'saturation'
  | 'color'
  | 'luminosity'
export type IImageSmoothingQuality = 'low' | 'medium' | 'high'
export type ILineCap = 'butt' | 'round' | 'square'
export type ILineJoin = 'round' | 'bevel' | 'miter'
export type ITextAlign = 'left' | 'right' | 'center' | 'start' | 'end'
export type ITextBaseline = 'top' | 'hanging' | 'middle' | 'alphabetic' | 'ideographic' | 'bottom'
export type ITextRendering = 'auto' | 'optimizeSpeed' | 'optimizeLegibility' | 'geometricPrecision'
export type IOrigin = 'none' | 'center' | { x: number; y: number }

export interface IBaseProps {
  /** 描述当前文本方向的属性 */
  direction?: IDirection
  /** 使用内部方式描述颜色和样式的属性。默认值是 #000 （黑色）。 */
  fillStyle?: CSSColor | LineGradient
  /** 同fillStyle 使用内部方式描述颜色和样式的属性。默认值是 #000 （黑色）。 */
  color?: CSSColor | LineGradient
  /** 提供模糊、灰度等过滤效果的属性。它类似于 CSS filter 属性，并且接受相同的函数。 */
  filter?: string
  /** 描述绘制文字时，当前字体样式的属性。使用和 CSS font 规范相同的字符串值。 */
  font?: string
  /** 指定如何使用字体字距调整信息。该属性对应于font-kerningCSS 属性。 */
  fontKerning?: IFontKerning
  /** 实验性!!! 在绘制文本时如何扩展或压缩字体。 */
  fontStretch?: IFontStretch
  /** 实验性!!! 呈现文本的替代大写形式。 */
  fontVariantCaps?: IFontVariantCaps
  /** 设置图形和图片透明度的属性。数值的范围从 0.0（完全透明）到 1.0（完全不透明）。 */
  globalAlpha?: number
  /** 同globalAlpha。 设置图形和图片透明度的属性。数值的范围从 0.0（完全透明）到 1.0（完全不透明）。 */
  alpha?: number
  /** 设置要在绘制新形状时应用的合成操作的类型，其中 type 是用于标识要使用的合成或混合模式操作的字符串。 */
  globalCompositeOperation?: IGlobalCompositeOperation
  /** 设置图片是否平滑的属性，true 表示图片平滑（默认值），false 表示图片不平滑。当我们获取 imageSmoothingEnabled 属性值时，它会返回最新设置的值。 */
  imageSmoothingEnabled?: boolean
  /** 用于设置图像平滑度的属性。 */
  imageSmoothingQuality?: IImageSmoothingQuality
  /** 绘制文本时字母之间的间距。 */
  letterSpacing?: CSSLength
  /** 绘制每一条线段末端的属性。有 3 个可能的值，分别是：butt, round and square。默认值是 butt。 */
  lineCap?: ILineCap
  /** 设置虚线偏移量的属性，例如可以实现“蚂蚁线“的效果。 */
  lineDashOffset?: number
  /** 设置 2 个长度不为 0 的相连部分（线段、圆弧、曲线）如何连接在一起的属性（长度为 0 的变形部分，其指定的末端和控制点在同一位置，会被忽略）。 */
  lineJoin?: ILineJoin
  /** 设置线段厚度的属性（即线段的宽度）。 */
  lineWidth?: number
  /** 设置斜接面限制比例的属性。当获取属性值时，会返回当前的值（默认值是10.0 ）。当给属性赋值时，0、负数、 Infinity 和 NaN 都会被忽略；除此之外都会被赋予一个新值。 */
  miterLimit?: number
  /** 描述模糊效果程度的属性；它既不对应像素值也不受当前转换矩阵的影响。默认值是 0。 */
  shadowBlur?: number
  /** 描述阴影颜色的属性。 */
  shadowColor?: CSSColor
  /** 描述阴影水平偏移距离的属性。 */
  shadowOffsetX?: number
  /** 描述阴影垂直偏移距离的属性。 */
  shadowOffsetY?: number
  /** 描述画笔（绘制图形）颜色或者样式的属性。默认值是 #000 (black)。 */
  strokeStyle?: CSSColor | LineGradient
  /** 文本的对齐方式的属性。注意，该对齐是基于 CanvasRenderingContext2D.fillText 方法的 x 的值。所以如果 textAlign="center"，那么该文本将画在 x-50%*width。 */
  textAlign?: ITextAlign
  /** 描述绘制文本时，当前文本基线的属性。 */
  textBaseline?: ITextBaseline
  /** 实验性!!! 向渲染引擎提供有关渲染文本时要优化哪些内容的信息。 */
  textRendering?: ITextRendering
  /** 绘制文本时单词之间的间距。 */
  wordSpacing?: CSSLength
  /** 是否填充内部颜色，默认为true，为false时，只绘制线段 */
  fill?: boolean
  /** 图形水平方向位置 */
  x?: number
  /** 图形垂直方向位置 */
  y?: number
  /** 图形旋转角度 */
  rotate?: number
  /** 图形缩放比例 */
  scale?: { x: number; y: number }
  /** 图形水平方向缩放 */
  scaleX?: number
  /** 图形垂直方向缩放 */
  scaleY?: number
  /** 图形倾斜转换 */
  skew?: { x: number; y: number }
  /** 图形水平方向倾斜转换 */
  skewX?: number
  /** 图形垂直方向倾斜转换 */
  skewY?: number
  /** 图形变换中心点 */
  origin?: IOrigin
}

export interface IBoundBox {
  top: number
  left: number
  right: number
  bottom: number
  width: number
  height: number
}


/**
 * 图形基础参数
 */
export abstract class BaseGraph {
  public defaultOption: IBaseProps = {
    direction: 'ltr',
    fillStyle: '#000000',
    filter: 'none',
    font: '10px sans-serif',
    fontKerning: 'auto',
    fontStretch: 'normal',
    fontVariantCaps: 'normal',
    globalAlpha: 1,
    globalCompositeOperation: 'source-over',
    imageSmoothingEnabled: true,
    imageSmoothingQuality: 'low',
    letterSpacing: '0px',
    lineCap: 'butt',
    lineDashOffset: 0,
    lineJoin: 'miter',
    lineWidth: 1,
    miterLimit: 10,
    shadowBlur: 0,
    shadowColor: 'rgba(0, 0, 0, 0)',
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    strokeStyle: '#000000',
    textAlign: 'start',
    textBaseline: 'alphabetic',
    textRendering: 'auto',
    wordSpacing: '0px'
  }
  public option: IBaseProps = {}
  public type = 'base'
  public isFill: boolean = true
  private _x: number = 0
  private _y: number = 0
  private _rotate: number = 0
  private _scaleX: number = 1
  private _scaleY: number = 1
  private _skewX: number = 0
  private _skewY: number = 0
  private _origin: IOrigin = 'none'
  public ctx = null
  public matrix: Matrix = new Matrix()
  public path = new Path2D()
  constructor(props?: IBaseProps) {
    this.option = props
    this.setOption()
    this.isFill = props?.fill == undefined ? this.isFill : props?.fill
    this._x = props?.x || this._x
    this._y = props?.y || this._y
    this._rotate = props?.rotate || this._rotate
    this._scaleX = props?.scale?.x || props?.scaleX || this._scaleX
    this._scaleY = props?.scale?.y || props?.scaleY || this._scaleY
    this._skewX = props?.skew?.x || props?.skewX || this._skewX
    this._skewY = props?.skew?.y || props?.skewY || this._skewY
    this._origin = props?.origin || this._origin
    this.setTransform()
  }
  public setOption(props?: IBaseProps) {
    this.defaultOption = { ...this.defaultOption, ...props, ...this.option }
    // 处理一下color和alpha
    this.defaultOption.fillStyle =
      this.option?.fillStyle || this.option?.color || props?.fillStyle || props?.color || this.defaultOption.fillStyle
    this.defaultOption.strokeStyle = this.option?.strokeStyle || props?.strokeStyle || this.defaultOption.fillStyle
    this.defaultOption.globalAlpha =
      this.option?.globalAlpha ||
      this.option?.alpha ||
      props?.globalAlpha ||
      props?.alpha ||
      this.defaultOption.globalAlpha
  }
  /**
   * 设置当前图形的样式
   * @param ctx
   */
  public initStyle(ctx: CanvasRenderingContext2D) {
    ctx.direction = this.defaultOption.direction
    ctx.fillStyle =
      typeof this.defaultOption.fillStyle == 'string'
        ? this.defaultOption.fillStyle
        : this.defaultOption.fillStyle.get()
    ctx.filter = this.defaultOption.filter
    ctx.font = this.defaultOption.font
    ctx.fontKerning = this.defaultOption.fontKerning
    // @ts-ignore
    ctx.fontStretch = this.defaultOption.fontStretch
    // @ts-ignore
    ctx.fontVariantCaps = this.defaultOption.fontVariantCaps
    ctx.globalAlpha = this.defaultOption.globalAlpha
    ctx.globalCompositeOperation = this.defaultOption.globalCompositeOperation
    ctx.imageSmoothingEnabled = this.defaultOption.imageSmoothingEnabled
    ctx.imageSmoothingQuality = this.defaultOption.imageSmoothingQuality
    // @ts-ignore
    ctx.letterSpacing = this.defaultOption.letterSpacing
    ctx.lineCap = this.defaultOption.lineCap
    ctx.lineDashOffset = this.defaultOption.lineDashOffset
    ctx.lineJoin = this.defaultOption.lineJoin
    ctx.lineWidth = this.defaultOption.lineWidth
    ctx.miterLimit = this.defaultOption.miterLimit
    ctx.shadowBlur = this.defaultOption.shadowBlur
    ctx.shadowColor = this.defaultOption.shadowColor
    ctx.shadowOffsetX = this.defaultOption.shadowOffsetX
    ctx.shadowOffsetY = this.defaultOption.shadowOffsetY
    ctx.strokeStyle =
      typeof this.defaultOption.strokeStyle == 'string'
        ? this.defaultOption.strokeStyle
        : this.defaultOption.strokeStyle.get()
    ctx.textAlign = this.defaultOption.textAlign
    ctx.textBaseline = this.defaultOption.textBaseline
    // @ts-ignore
    ctx.textRendering = this.textRendering
  }
  /**
   * 绘制图形
   * @param ctx
   */
  public draw(ctx: CanvasRenderingContext2D) {
    ctx.save()
    let a = ctx.getTransform()
    let m1 = [a.a, a.b, a.c, a.d, a.e, a.f]
    let m2: Array<DOMMatrix2DInit> = this.matrix.multiply(m1, this.matrix.matrix) as any
    ctx.setTransform(...m2)
    ctx.beginPath()
    this.initStyle(ctx)
    this.drawPath(ctx)
    ctx.restore()
  }
  /**
   * 设置变换
   */
  public setTransform() {
    let offsetX = 0
    let offsetY = 0
    if (typeof this._origin == 'string') {
      if (this._origin == 'center') {
        let sphere = this.getBoundBox()
        offsetX = sphere.width / 2
        offsetY = sphere.height / 2
      }
    } else {
      offsetX = this._origin.x
      offsetY = this._origin.y
    }
    this.matrix.reset()
    this.matrix.transform([
      this.matrix.translateMatrix(-offsetX, -offsetY),
      this.matrix.scaleMatrix(this._scaleX, this._scaleY),
      this.matrix.rotateMatrix(this._rotate),
      this.matrix.skewMatrix(this._skewX, this._skewY),
      this.matrix.translateMatrix(this.x + offsetX, this.y + offsetY)
    ])
  }
  /**
   * 设置图形位置
   * @param x 水平方向位置
   * @param y 垂直方向位置
   */
  public translate(x: number, y: number) {
    this._x = x
    this._y = y
    this.setTransform()
  }
  /**
   * 水平方向位置
   */
  public set x(value: number) {
    this._x = value
    this.setTransform()
  }
  public get x() {
    return this._x
  }
  /**
   * 垂直方向位置
   */
  public set y(value: number) {
    this._y = value
    this.setTransform()
  }
  public get y() {
    return this._y
  }
  /**
   * 图形旋转
   */
  public set rotate(angle: number) {
    this._rotate = angle
    this.setTransform()
  }
  public get rotate() {
    return this._rotate
  }
  /**
   * 图形缩放
   */
  public scale(x: number, y?: number) {
    this._scaleX = x
    this._scaleY = y || x
    this.setTransform()
  }
  /**
   * 获取图形缩放值
   * @returns x, y
   */
  public getScale() {
    return { x: this._scaleX, y: this._scaleY }
  }
  /**
   * 图像水平方向缩放
   */
  public set scaleX(value: number) {
    this._scaleX = value
    this.setTransform()
  }
  public get scaleX() {
    return this._scaleX
  }
  /**
   * 图像垂直方向缩放
   */
  public set scaleY(value: number) {
    this._scaleY = value
    this.setTransform()
  }
  public get scaleY() {
    return this._scaleY
  }
  public skew(skewX: number, skewY?: number) {
    this._skewX = skewX
    this._skewY = skewY || skewX
    this.setTransform()
  }
  public getSkew() {
    return { x: this._skewX, y: this._skewY }
  }
  public set skewX(value: number) {
    this._skewX = value
    this.setTransform()
  }
  public get skewX() {
    return this._skewY
  }
  public set skewY(value: number) {
    this._skewY = value
    this.setTransform()
  }
  public get skewY() {
    return this._skewY
  }
  public set origin(value: IOrigin) {
    this._origin = value
    this.setTransform()
  }
  public get origin() {
    return this._origin
  }
  protected getBaseCloneAttr() {
    return {
      x: this._x,
      y: this._y,
      rotate: this._rotate,
      scaleX: this._scaleX,
      scaleY: this._scaleY,
      skewX: this._skewX,
      skewY: this._skewY,
      origin: this._origin
    }
  }
  abstract setPath(): void
  abstract clone(): BaseGraph
  abstract drawPath(ctx: CanvasRenderingContext2D): void
  abstract getBoundBox(): IBoundBox
}
